<!--
 * @Description: 虚拟列表
 * @Author: liyujie
 * @Date: 2022/2/23
 * @Last Modified by: liyujie
 * @Last Modified time: 2022/2/23
-->
<template>
    <section class="wrapper">
        <!--滚动容器-->
        <div
            class="scroll-wrapper"
            ref="scrollWrapper"
            @scroll="handleScroll"
        >
            <!--占位用于形成滚动条-->
            <div
                class="blank"
                :style="{ height: listHeight + 'px' }"
            />
            <!--列表-->
            <div
                class="list"
                :style="{transform: getTransform}"
            >
                <div class="item" v-for="(item,index) in visibleData" :key="index">
                    {{ item.message }}
                </div>
            </div>
        </div>
    </section>
</template>

<script>
export default {
    name: 'VirtualList',
    data() {
        return {
            listData: [],
            // 列表item高度
            itemSize: 60,
            // 可视区域高度
            screenHeight: 0,
            // 偏移量
            startOffset: 0,
            // 起始索引
            startIndex: 0,
            // 结束索引
            endIndex: null,
        };
    },
    computed: {
        // 列表的总高度
        listHeight() {
            return this.listData.length * this.itemSize;
        },

        // 一屏可显示的列表项数
        visibleCount() {
            return Math.ceil(this.screenHeight / this.itemSize);
        },

        // 真实显示的数据
        visibleData() {
            return this.listData.slice(this.startIndex, this.endIndex);
        },

        // 列表纵向偏移
        getTransform(){
            return `translateY(${this.startOffset}px)`;
        },
    },
    mounted() {
        for (let i = 0; i < 10000; i++) {
            this.listData.push({ message: `第${i + 1}条数据` });
        }
        // 获取滚动容器高度
        this.screenHeight = this.$refs.scrollWrapper.clientHeight;
        this.startIndex = 0;
        // 结束索引为 开始索引 + 一屏显示个数
        this.endIndex = this.startIndex + this.visibleCount;
    },
    methods: {
        /**
         * 滚动事件
         * @param $event
         */
        handleScroll($event) {
            let scrollTop = this.$refs.scrollWrapper.scrollTop;

            this.startIndex = Math.floor(scrollTop / this.itemSize);
            this.endIndex = this.startIndex + this.visibleCount;
            this.startOffset = Math.floor(scrollTop / this.itemSize) * this.itemSize;
        }
    }
};
</script>

<style scoped lang="scss">
.wrapper {
    overflow: hidden;
}
.scroll-wrapper {
    margin: 50px 200px;
    width: 400px;
    height: 800px;
    border: 1px solid #ccc;
    overflow: auto;
    position: relative;
    .blank {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        z-index: -1;
    }
    .list {
        .item {
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-bottom: 1px solid #ccc;
            background: #42b983;
        }
    }
}
</style>
